package bll

import (
	log "auth/collector/logger"
	"auth/errors"
	"auth/event"
	"auth/model"
	"auth/store"
	"auth/store/postgres"
	"github.com/casbin/casbin/v2"
	"strconv"
	"sync"
)

type casbinService struct {
	iCasbin store.ICasbin
}

var CasbinService = &casbinService{}

func (a *casbinService) init() func() {
	a.iCasbin = postgres.CasbinService
	return func() {}
}

func (a *casbinService) onEvent(*event.Data) {}

var (
	cachedEnforcer *casbin.CachedEnforcer
	once           sync.Once
)

func (a *casbinService) Casbin() *casbin.CachedEnforcer {
	var err error
	once.Do(func() {
		cachedEnforcer, err = a.iCasbin.Casbin()
		if err != nil {
			return
		}
	})
	return cachedEnforcer
}

func (a *casbinService) UpdateCasbinApi(oldPath string, newPath string, oldMethod string, newMethod string) error {
	var err error
	err = a.iCasbin.UpdateCasbinApi(oldPath, newPath, oldMethod, newMethod)
	if err != nil {
		log.Error(err)
		return err
	}
	enforcer := a.Casbin()
	err = enforcer.InvalidateCache()
	if err != nil {
		log.Error(err)
		return err
	}
	return err
}

func (a *casbinService) GetPolicyPathByAuthorityId(in *model.PolicyPathRequest) (*model.PolicyPathResponse, error) {
	var (
		out = model.PolicyPathResponse{}
	)
	enforcer := a.Casbin()
	authorityId := strconv.Itoa(int(in.AuthorityId))
	list := enforcer.GetFilteredPolicy(0, authorityId)
	for _, v := range list {
		out.Paths = append(out.Paths, model.CasbinInfo{
			Path:   v[1],
			Method: v[2],
		})
	}
	return &out, nil
}

func (a *casbinService) UpdateCasbin(in *model.CasbinUpdateRequest) error {
	authorityId := strconv.Itoa(int(in.AuthorityId))
	a.ClearCasbin(0, authorityId)
	rules := [][]string{}
	for _, v := range in.CasbinInfos {
		rules = append(rules, []string{authorityId, v.Path, v.Method})
	}
	enforcer := a.Casbin()
	success, _ := enforcer.AddPolicies(rules)
	if !success {
		return errors.New("存在相同api,添加失败,请联系管理员")
	}
	err := cachedEnforcer.InvalidateCache()
	if err != nil {
		return err
	}
	return nil
}

func (a *casbinService) ClearCasbin(v int, p ...string) bool {
	enforcer := a.Casbin()
	success, _ := enforcer.RemoveFilteredPolicy(v, p...)
	return success
}
